/*
	Siphash implementation using arrays
	Elie Bursztein (hello@elie.im)
	Free for any use.
*/
var SipHashArray = (function() {
    function _add(a, b) {
        var rl = a[0] + b[0];
        a[1] = a[1] + b[1] + (rl / 2 >>> 31) >>> 0;
        a[0] = rl >>> 0;
    }

    function _xor(a, b) {
        a[1] ^= b[1]; a[1] >>>= 0;
        a[0] ^= b[0]; a[0] >>>= 0;
    }


    function _rotl(a, n) {
        var v0 = a[0] << n | a[1] >>> (32 - n);
        a[1] = a[1] << n | a[0] >>> (32 - n);
        a[0] = v0;
    }

    function _rotl32(a) {
        var al = a[0];
        a[0] = a[1];
        a[1] = al;
    }

    function _compress(v0, v1, v2, v3) {
        _add(v0, v1);
        _add(v2, v3);
        _rotl(v1, 13);
        _rotl(v3, 16);
        _xor(v1, v0);
        _xor(v3, v2);
        _rotl32(v0);
        _add(v2, v1);
        _add(v0, v3);
        _rotl(v1, 17);
        _rotl(v3, 21);
        _xor(v1, v2);
        _xor(v3, v0);
        _rotl32(v2);
    }

    function _get_int(a, offset) {
        return a.charCodeAt(offset + 3) << 24 |
               a.charCodeAt(offset + 2) << 16 |
               a.charCodeAt(offset + 1) << 8 |
               a.charCodeAt(offset);
    }

    function hash(key, m) {
    	var v0 = [key[0] >>> 0 ^ 0x70736575, key[1] >>> 0 ^ 0x736f6d65], 
        v1 = [key[2] >>> 0 ^ 0x6e646f6d, key[3] >>> 0 ^ 0x646f7261],
        v2 = [key[0] >>> 0 ^ 0x6e657261, key[1] >>> 0 ^ 0x6c796765],
        v3 = [key[2] >>> 0 ^ 0x79746573, key[3] >>> 0 ^ 0x74656462],
        mi, mp = 0, buf = [], i = 0;

    	buf[7]= m.length;
    	j = buf[7] - 7;

        for (;mp < j;) {
            mi = [ _get_int(m, mp), _get_int(m, mp + 4)];
            _xor(v3, mi);
            _compress(v0, v1, v2, v3);
            _compress(v0, v1, v2, v3);
            _xor(v0, mi);
            mp += 8;
        }

        for (;mp < j + 7;) buf[i++] = m.charCodeAt(mp++);
        
        for (;i < 7;) buf[i++] = 0;
        
        mi = [ buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0],
                buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]
             ];
       _xor(v3, mi);
       _compress(v0, v1, v2, v3);
       _compress(v0, v1, v2, v3);
       _xor(v0, mi);
       _xor(v2, [0xff, 0 ]);
       _compress(v0, v1, v2, v3);
       _compress(v0, v1, v2, v3);
       _compress(v0, v1, v2, v3);
       _compress(v0, v1, v2, v3);
       i = v0[0]^v1[0]^v2[0]^v3[0];
       j = v0[1]^v1[1]^v2[1]^v3[1];
       return [i >>>0, j>>>0];
    }

    function str2key(a) {
        return [_get_int(a, 0), _get_int(a, 4),
                _get_int(a, 8), _get_int(a, 12)];
    }

    function hex(key, m) {
        var r = hash(key, m);
        return ("0000000" + r[1].toString(16)).substr(-8) +
               ("0000000" + r[0].toString(16)).substr(-8);
    }

    function uint(key, m) {
        var r = hash(key, m);
        return (r[1] & 0x1fffff) * 0x100000000 + r[0];
    }

    return {
        str2key: str2key,
        hash: hash,
        hex: hex,
        uint: uint
    };
})();